/*
 * funcionesHilo.c
 *
 *  Created on: 10/11/2011
 *      Author: utn_so
 */
#include "planificador.h"
// La funcion que es utilizada en un Hilo
// debera de retornar un puntero sin tipo
int32_t hilo_crear(long hiloId) {
	pthread_t hilito;
	int32_t vRetorno;
	vRetorno=pthread_create(&hilito, NULL, hilo_Funcion, (void *)hiloId);
	if (vRetorno) {
		printf("ERROR; return code from pthread_create() is %d\n", vRetorno);
		exit(-1);
	}
	return vRetorno;
}
int32_t hilo_crear2(long hiloId) {
	pthread_t hilito;
	int32_t vRetorno;
	vRetorno=pthread_create(&hilito, NULL, hilo_Funcion2, (void *)hiloId);
	if (vRetorno) {
		printf("ERROR; return code from pthread_create() is %d\n", vRetorno);
		exit(-1);
	}
	return vRetorno;
}
void *hilo_Funcion2(void *threadid) {
	long tid;
	// Aqui se castea lo que le fue pasado
	// al hilo como atributos
	tid = (long)threadid;
	if(memcmp(stConfig.cModoInicio,"L",strlen("L"))==0){

		//variables de las lsitas del select
		struct listaSelect *punteroAuxSelect=NULL;

		//variables de NIPC
		struct nipc_protocol punteroNIPCRecibir;

		//variables de sockets/select

		fd_set conjuntoDeDescriptores;
		int maximo;

		//variables de listas a escribir/leer
		struct estructuraEscribir *punteroANodoNuevoEscribir=NULL;
		struct estructuraLeer *punteroANodoNuevoLeer=NULL;


		pthread_mutex_lock(&mutexArchConfig);
			socketGlobal = socketInetServidor_abrir (stConfig.iPuertoEscucha);
				//socketGlobal = socketInet_abrir (7045);
				if (socketGlobal == -1) {
					perror ("Error al abrir servidor");
					exit (-1);
				}
		pthread_mutex_unlock(&mutexArchConfig);



		while (1) {
			descriptoresCaidos_detectar (&punteroFteSelect,&punteroFinSelect);
			FD_ZERO (&conjuntoDeDescriptores);
			FD_SET (socketGlobal, &conjuntoDeDescriptores);
			punteroAuxSelect=punteroFteSelect;
			while(punteroAuxSelect!=NULL) {
				FD_SET (punteroAuxSelect->descriptor, &conjuntoDeDescriptores);
				punteroAuxSelect=punteroAuxSelect->siguiente;
			}
			maximo = descriptorMaximo_detectar (&punteroFteSelect,&punteroFinSelect);
			if (maximo < socketGlobal)
				maximo = socketGlobal;
			select (maximo + 1, &conjuntoDeDescriptores, NULL, NULL, NULL);
			punteroAuxSelect=punteroFteSelect;
			while(punteroAuxSelect!=NULL) {
				if (FD_ISSET (punteroAuxSelect->descriptor, &conjuntoDeDescriptores)) {
					NIPC_recibir(&(punteroAuxSelect->descriptor),&punteroNIPCRecibir);
					switch(punteroNIPCRecibir.type) {
						case '0':
							printf("Recibido: [type: %c]; ",punteroNIPCRecibir.type);
							printf("[payloadLength: %d]; ",punteroNIPCRecibir.payloadLength);
							printf("[payload: %s]; ",punteroNIPCRecibir.payload);
							printf("[numeroSector: %d]\n",punteroNIPCRecibir.numeroSector);
							//en cliente nuevo se manda el tipo (fs 1 y ppd 0)y el status(0 ok 1 down)
							//si el payload del handshake esta vacio,es un fs
							if(memcmp(punteroNIPCRecibir.payload,"id",strlen("id"))==0) {
								punteroAuxSelect->tipo=0;
								punteroAuxSelect->status=1;
							} else {
								punteroAuxSelect->tipo=1;
								punteroAuxSelect->status=0;
							}

							NIPC_enviar(punteroAuxSelect->descriptor,'0',strlen("hola aye"),"hola aye",0);
							//memset(punteroNIPCRecibir,'\0',sizeof(struct nipc_protocol));
							memset(punteroNIPCRecibir.payload,'\0',512);

							//memset(punteroNIPCenviar,'\0',sizeof(struct nipc_protocol));
							break;
						case '1':
							nodoEscritura_crear(&punteroANodoNuevoEscribir,punteroNIPCRecibir.numeroSector,punteroNIPCRecibir.payload);
							colaEscritura_meter(&punteroFrenteEscribir,&punteroFinEscribir, &punteroANodoNuevoEscribir);
							//memset(punteroNIPCRecibir,'\0',sizeof(struct nipc_protocol));
							memset(punteroNIPCRecibir.payload,'\0',512);
							break;
						case '2':

							nodoLectura_crear(&punteroANodoNuevoLeer,punteroNIPCRecibir.numeroSector, 0,0);
							punteroANodoNuevoLeer->descriptorAEnviar=punteroAuxSelect->descriptor;
							colaLectura_meter(&punteroFrenteLeer,&punteroFinLeer, &punteroANodoNuevoLeer);
							printf("Se RECIBIO del descriptor %d, el sector %d\n", punteroAuxSelect->descriptor, punteroNIPCRecibir.numeroSector);
							memset(punteroNIPCRecibir.payload,'\0',512);

							break;
						case '3':

				//			NIPC_enviar(fs_elegirAEnviar(),punteroNIPCRecibir.type,punteroNIPCRecibir.payloadLength,punteroNIPCRecibir.payload,punteroNIPCRecibir.numeroSector);
							//memset(punteroNIPCenviar,'\0',sizeof(struct nipc_protocol));
						//	memset(punteroNIPCRecibir,'\0',sizeof(struct nipc_protocol));
					//		memset(punteroNIPCRecibir.payload,'\0',512);

							break;
						case '4':
																//aqui iria lo de sincronizacion
							break;
						default:
							break;
					}
					//memset(punteroNIPCRecibir,'\0',sizeof(struct nipc_protocol));
					//free(punteroNIPCRecibir->payload);
					//free(punteroNIPCRecibir);

				}

			punteroAuxSelect=punteroAuxSelect->siguiente;
		}
		if (FD_ISSET (socketGlobal, &conjuntoDeDescriptores)) {
			clienteNuevo_agregar (socketGlobal, &punteroFteSelect,&punteroFinSelect);
		}
		}
	}else//socketGlobal = socketInet_abrir (stConfig.iPuertoEscucha);
		//socketGlobal = socketInet_abrir (4024);
		/* Se lee el número de cliente, dato que nos da el servidor. Se escribe
		 * dicho número en pantalla.*/

			if(memcmp(stConfig.cModoInicio,"C",strlen("C"))==0){
				char*cHs=calloc(1,sizeof(stConfig.iCabezal)*3+2);//el 2 es por las dos comas maas


				pthread_mutex_lock(&mutexArchConfig);
					socketGlobal = socketInetCliente_abrir (stConfig.iPuertoEscucha);
						//socketGlobal = socketInet_abrir (7045);
						if (socketGlobal == -1) {
							perror ("Error al abrir servidor");
							exit (-1);
						}
				pthread_mutex_unlock(&mutexArchConfig);


									pthread_mutex_lock(&mutexEnviar);
									NIPC_enviar(socketGlobal,'0',strlen("id 1"),"id 1",0);
									pthread_mutex_unlock(&mutexEnviar);

									struct nipc_protocol punteroNIPCaRecibir;
									pthread_mutex_lock(&mutexRecibir);
									NIPC_recibir(&socketGlobal,&punteroNIPCaRecibir);
									//memset(punteroNIPCaRecibir,'\0',sizeof(struct nipc_protocol));
									memset(punteroNIPCaRecibir.payload,'\0',512);

									pthread_mutex_unlock(&mutexRecibir);

									sprintf(cHs,"%d,%d,%d",stConfig.iCabezal,stConfig.iCantidadPistas,stConfig.iSectoresPista);
									pthread_mutex_lock(&mutexEnviar);
									NIPC_enviar(socketGlobal,'T',strlen(cHs),cHs,0);
									pthread_mutex_unlock(&mutexEnviar);
									free(cHs);

									pthread_mutex_lock(&mutexRecibir);
									NIPC_recibir(&socketGlobal,&punteroNIPCaRecibir);
									pthread_mutex_unlock(&mutexRecibir);
					while((punteroNIPCaRecibir.type!='X')){
			//Depende el tipo recibido lo encola donde corresponda

			//printf("punteroNIPCaRecibir->type %c\n",punteroNIPCaRecibir->type);
			switch (punteroNIPCaRecibir.type) {
				case '5': //Cola escritura
					pthread_mutex_lock(&mutexInfoSector2);
					nodoEscritura_crear(&punteroEstructuraEscribir, punteroNIPCaRecibir.numeroSector, punteroNIPCaRecibir.payload);//TODO sem1
					pthread_mutex_unlock(&mutexInfoSector2);
					pthread_mutex_lock(&mutexPtroFteEscribir);
					colaEscritura_meter(&punteroFrenteEscribir,&punteroFinEscribir,&punteroEstructuraEscribir); //TODO sem3
					pthread_mutex_unlock(&mutexPtroFteEscribir);
					//printf("Se encoló en cola de escritura");
					break;
				case '6': //Cola lectura
					nodoLectura_crear(&punteroEstructuraLeer, punteroNIPCaRecibir.numeroSector, 0,0);
					colaLectura_meter(&punteroFrenteLeer,&punteroFinLeer,&punteroEstructuraLeer); //No deberia ir el ultimo puntero, modificar
					//printf("Se encoló en cola de lectura");
					break;
				case '7': //Cola leer para Sincro
					nodoLectura_crear(&punteroEstructuraSincro, punteroNIPCaRecibir.numeroSector, 0,1);
					pthread_mutex_lock(&mutexSincro);
					colaLectura_meter(&punteroFrenteLeer,&punteroFinLeer,&punteroEstructuraSincro); //No deberia ir el ultimo puntero, modificar
					pthread_mutex_unlock(&mutexSincro);
					break;

				default:
					break;
			}

/*
			printf("Recibido: [type: %c]; ",punteroNIPCaRecibir->type);
			printf("[payloadLength: %d]; ",punteroNIPCaRecibir->payloadLength);
			printf("[payload: %s]; ",punteroNIPCaRecibir->payload);*/
			//printf("[numeroSector: %d]\n",punteroNIPCaRecibir->numeroSector);


			//sleep (1);

		//	memset(punteroNIPCaRecibir,'\0',sizeof(struct nipc_protocol));
			memset(punteroNIPCaRecibir.payload,'\0',512);

		//	pthread_mutex_lock(&mutexRecibir);
			NIPC_recibir(&socketGlobal,&punteroNIPCaRecibir);
		//	pthread_mutex_unlock(&mutexRecibir);
		}


	}
	pthread_exit(&valorRetornoFinHilo);
		return threadid;
}
//hilo_esperarFinalizacion
void *hilo_Funcion(void *threadid) {
	long tid;
	struct nipc_protocol punteroNIPC;
	int32_t cleanInicial, cleanFinal;
	char bufferVacio[512];
	char dondeApuntaElCabezal[50];
	struct estructuraEscribir *punteroANodoNuevoEscribir=NULL;
	struct estructuraLeer *punteroANodoNuevoLeer=NULL;

	memset(bufferVacio,'\0',512);
	// Aqui se castea lo que le fue pasado
	// al hilo como atributos
	tid = (long)threadid;
	//printf("Hola esto se ejecuta en el hilo #%ld!\n", tid);
	while(1) {
		printf("Esperando conexion...\n");
		tamanhoEstructuraSocket = sizeof(estructuraSocketConsolaPPD);
		if ((descriptorSocketConsolaPPD = accept(descriptorSocketPlanificador, (struct sockaddr *)&estructuraSocketConsolaPPD, &tamanhoEstructuraSocket)) == -1) {
			perror("accept");
			exit(1);
		}
		printf("conectada.\n");
		hecho = 0;

		NIPC_recibir(&descriptorSocketConsolaPPD, &punteroNIPC);
		while(punteroNIPC.type != 'S'){
			//void NIPC_preparar(struct nipc_protocol**punteroNIPC,  char type,int payloadLength,char *payload,int numeroSector);
		   //void NIPC_enviar(struct nipc_protocol**punteroNIPC,int descriptorSocket,char type,int payloadLength,char *payload,int numeroSector);

		//	mensaje_recibir(descriptorSocketConsolaPPD,hecho);
		//	mensaje_detectar();

			switch(punteroNIPC.type){
				case 'I':
					memset(&punteroNIPC,'\0',sizeof(struct nipc_protocol));
					//memset(punteroNIPC.payload,'\0',512);
					memset(dondeApuntaElCabezal,'\0',sizeof(dondeApuntaElCabezal));
					sprintf(dondeApuntaElCabezal,"%d:%d (sector logico %d)",posicionCabezal/stConfig.iSectoresPista,posicionCabezal % stConfig.iSectoresPista, posicionCabezal);

					NIPC_enviar(descriptorSocketConsolaPPD,'I',512,dondeApuntaElCabezal,0);
					break;
				case 'R':
					meterNodosParaElTrace(punteroNIPC.payload);
					//memset(punteroNIPC.payload,'\0',512);
					break;
				case 'J':
					printf("Usted cambio el tiempo de lectura de: %d",stConfig.iTiempoLectura);
					sscanf(punteroNIPC.payload,"%d",&stConfig.iTiempoLectura);
					//memset(punteroNIPC.payload,'\0',512);
					printf("por: %d\n",stConfig.iTiempoLectura);
					break;
				case 'C':
					sscanf(punteroNIPC.payload,"<%d> <%d>",&cleanInicial,&cleanFinal);
					for(cleanInicial; cleanInicial <= cleanFinal; cleanInicial++){
						nodoEscritura_crear(&punteroANodoNuevoEscribir,cleanInicial,bufferVacio);
						colaEscritura_meter(&punteroFrenteEscribir,&punteroFinEscribir, &punteroANodoNuevoEscribir);
					}
					//memset(punteroNIPC,'\0',sizeof(struct nipc_protocol));
			//		memset(punteroNIPC.payload,'\0',512);
					break;
				default:
					break;
			}
			memset(&punteroNIPC,'\0',sizeof(struct nipc_protocol));
			NIPC_recibir(&descriptorSocketConsolaPPD, &punteroNIPC);
			//	if (!hecho)
		//		mensaje_enviar(descriptorSocketConsolaPPD, hecho);
		}
	//	while (!hecho);
		socket_cerrar(descriptorSocketConsolaPPD);
		exit(0);
	}
		pthread_exit(&valorRetornoFinHilo);
		return threadid;
	}
	// --- Aqui finaliza la funcion que
	// sera llamada en el hilo
/*
int32_t fs_elegirAEnviar(int32_t numeroSector) {
	struct estructuraLeer* backupAuxiliar;
		punteroFileSystem=punteroFteSelect;
		while(punteroFileSystem!=NULL) {
			if(punteroFileSystem->tipo==1) {
				backupAuxiliar=punteroFileSystem->punteroBackup;
				while(backupAuxiliar){
					if(backupAuxiliar->numeroSector==numeroSector){
						pthread_mutex_lock(&mutexBackup1);
						nodoDeColaBackup_eliminar(&backupAuxiliar,&(punteroFileSystem->punteroBackupFin),numeroSector);
						pthread_mutex_unlock(&mutexBackup1);
						return punteroFileSystem->descriptor;
					}
					backupAuxiliar=backupAuxiliar->siguiente;
				}

			}
			punteroFileSystem=punteroFileSystem->siguiente;
		}
return -1;
}
*/

void nodoDeColaBackup_eliminar(struct estructuraLeer **punteroBackup,struct estructuraLeer **punteroBackupFin,int32_t sector) {

	estructuraLeer *auxiliar;
	estructuraLeer *anterior;
	auxiliar = *punteroBackup;
	while(auxiliar && (auxiliar->numeroSector != sector)) {
		anterior = auxiliar;
		auxiliar = auxiliar->siguiente;
	}
	if ((auxiliar!=NULL)&&(*punteroBackup == auxiliar)){
		*punteroBackup = (*punteroBackup)->siguiente;

	}else{

		anterior->siguiente = auxiliar->siguiente;

	}
	if(auxiliar==*punteroBackupFin){
				*punteroBackupFin=anterior;
			}
	free(auxiliar);

	return;
}


void clienteNuevo_agregar (int servidor, struct listaSelect **punteroFteSelect,struct listaSelect **punteroFinSelect) {

	struct listaSelect *punteroEstructura;

	nodoSelect_crear(&punteroEstructura, conexionCliente_Aceptar (servidor));
	colaSelect_meter(&(*punteroFteSelect),&(*punteroFinSelect), &punteroEstructura);
	printf ("Aceptado cliente %d\n", (*punteroFinSelect)->descriptor);
	return;
}
int descriptorMaximo_detectar (struct listaSelect **punteroFteSelect,struct listaSelect **punteroFinSelect) {
	struct listaSelect *punteroAux=*punteroFteSelect;
	int max;
	if (punteroAux == NULL)
					return 0;
	max = punteroAux->descriptor;
	while(punteroAux != NULL) {
		if (punteroAux->descriptor > max)
									max = punteroAux->descriptor;
		punteroAux=punteroAux->siguiente;
	}
	return max;
}
void descriptoresCaidos_detectar (struct listaSelect **punteroFteSelect,struct listaSelect **punteroFinSelect) {
	struct listaSelect *punteroAux=*punteroFteSelect;
	if (*punteroFteSelect == NULL)
					return;
	while(punteroAux!=NULL) {
		if (punteroAux->descriptor != -1) {
			punteroAux=punteroAux->siguiente;
		} else {
			nodoDeColaSelect_eliminar(&(*punteroFteSelect),&(*punteroFinSelect),&punteroAux,punteroAux->descriptor);
		}
	}

}
int planificadores_buscar(){
	struct listaSelect *punteroAux=punteroFteSelect;
	if (punteroFteSelect == NULL){
		return 0;
	}
	while(punteroAux!=NULL) {
		if ((punteroAux->tipo == -1)||(punteroAux->tipo == 0)) {
			return 1;
		}else if(punteroAux->tipo == 1){
			if(punteroAux->siguiente == NULL){
				return 0;
			}else{
				punteroAux=punteroAux->siguiente;
			}
		}
	}

}
